#include "boundaryPatch.hpp"
#include "meshTypes.hpp"
#include "meshTypeFunctions.hpp"
#include "mesh_testFunctions.hpp"
#include <catch2/catch_test_macros.hpp>
#include <iostream>

using namespace hms;

TEST_CASE( "Creating boundary patch, checking ghost and internal cells",
	"[BoundaryPatch]"
){
	SECTION( "Unstructured mesh" ){
		auto mesh { testMeshRectangles(true) };
		
		BoundaryPatch left   { "left",   mesh, Array2i{ 8, 9} };
		BoundaryPatch right  { "right",  mesh, Array2i{ 4, 5} };
		BoundaryPatch top    { "top",    mesh, Array2i{10,11} };
		BoundaryPatch bottom { "bottom", mesh, Array2i{ 6, 7} };
		
		REQUIRE( (left  .domainCells() == Array2i{ 0, 1}).all() );
		REQUIRE( (right .domainCells() == Array2i{ 2, 3}).all() );
		REQUIRE( (top   .domainCells() == Array2i{ 1, 3}).all() );
		REQUIRE( (bottom.domainCells() == Array2i{ 0, 2}).all() );
		
		REQUIRE( (left  .ghostCells () == Array2i{ 8, 9}).all() );
		REQUIRE( (right .ghostCells () == Array2i{ 4, 5}).all() );
		REQUIRE( (top   .ghostCells () == Array2i{10,11}).all() );
		REQUIRE( (bottom.ghostCells () == Array2i{ 6, 7}).all() );

		SECTION( "Normal sign"){
			REQUIRE( bottom.normalSign() == 1 );
			REQUIRE( left  .normalSign() == 1 );
			REQUIRE( right .normalSign() == 1 );
			REQUIRE( top   .normalSign() == 1 );
		}
	}
	auto test = []( auto& mesh ){

		const BoundaryPatch
		& bottom { mesh.boundaryPatch("bottom") },
		& top    { mesh.boundaryPatch("top"   ) },
		& left   { mesh.boundaryPatch("left"  ) },
		& right  { mesh.boundaryPatch("right" ) };

		REQUIRE( (bottom.edges() == Array3i{ 0, 1, 2}).all() );
		REQUIRE( (   top.edges() == Array3i{12,13,14}).all() );
		REQUIRE( (  left.edges() == Array4i{15,19,23,27}).all() );
		REQUIRE( ( right.edges() == Array4i{18,22,26,30}).all() );

		REQUIRE( (bottom.domainCells() == Array3i{ 6, 7, 8}).all() );
		REQUIRE( (   top.domainCells() == Array3i{21,22,23}).all() );
		REQUIRE( (  left.domainCells() == Array4i{ 6,11,16,21}).all() );
		REQUIRE( ( right.domainCells() == Array4i{ 8,13,18,23}).all() );

		REQUIRE( (bottom.ghostCells() == Array3i{ 1, 2, 3}).all() );
		REQUIRE( (   top.ghostCells() == Array3i{26,27,28}).all() );
		REQUIRE( (  left.ghostCells() == Array4i{ 5,10,15,20}).all() );
		REQUIRE( ( right.ghostCells() == Array4i{ 9,14,19,24}).all() );

		using B = BoundaryLocation;
		SECTION("Location"){
			REQUIRE( bottom.location() == B::bottom );
			REQUIRE( left  .location() == B::left   );
			REQUIRE( right .location() == B::right  );
			REQUIRE( top   .location() == B::top    );
		}
		SECTION("Normal sign"){
			REQUIRE( bottom.normalSign() == -1 );
			REQUIRE( left  .normalSign() == -1 );
			REQUIRE( right .normalSign() ==  1 );
			REQUIRE( top   .normalSign() ==  1 );
		}
		SECTION("Outwards normal"){
			REQUIRE( bottom.outwardNormals(mesh).isApprox( Vector2s{ 0,-1}.replicate(1,3) ) );
			REQUIRE( left  .outwardNormals(mesh).isApprox( Vector2s{-1, 0}.replicate(1,4) ) );
			REQUIRE( right .outwardNormals(mesh).isApprox( Vector2s{ 1, 0}.replicate(1,4) ) );
			REQUIRE( top   .outwardNormals(mesh).isApprox( Vector2s{ 0, 1}.replicate(1,3) ) );
		}
	};
	SECTION( "UniMesh" ){
		auto mesh { uniMeshAs<UniMesh>(3, 4, 1, 1, true) };
		test(mesh);
	}
	SECTION( "RectMesh" ){
		auto mesh { uniMeshAs<RectMesh>(3, 4, 1, 1, true) };
		test(mesh);
	}
	SECTION( "StructMesh" ){
		auto mesh { uniMeshAs<StructMesh>(3, 4, 1, 1, true) };
		test(mesh);
	}
}